﻿using System.Linq.Expressions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Devonline.Database.TimeSeries.InfluxDB;

/// <summary>
/// influxdb 数据库 flux 通用操作服务
/// </summary>
public sealed class InfluxDBDataService : InfluxDBService, ITimeSeriesDataService, ITimeSeriesService, INoSQLDataService, INoSQLService, IDisposable
{
    /// <summary>
    /// Http Context
    /// </summary>
    private readonly HttpContext _httpContext;
    /// <summary>
    /// Http Request
    /// </summary>
    private readonly HttpRequest _request;
    /// <summary>
    /// Http 配置
    /// </summary>
    private readonly HttpSetting _httpSetting;

    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="endpoint"></param>
    /// <param name="httpContextAccessor"></param>
    /// <param name="httpSetting"></param>
    public InfluxDBDataService(ILogger<InfluxDBDataService> logger, IInfluxDBEndpoint endpoint, IHttpContextAccessor httpContextAccessor, HttpSetting httpSetting) : base(logger, endpoint)
    {
        _httpContext = httpContextAccessor.HttpContext!;
        _request = httpContextAccessor.HttpContext!.Request;
        _httpSetting = httpSetting;
        UserName = GetUserName();
        DataIsolate = httpSetting.DataIsolate;
        DataIsolateId = GetDataIsolateId();
    }

    /// <summary>
    /// 当前登录的用户编号
    /// </summary>
    public string UserId => GetUserId() ?? throw new UnauthorizedAccessException();
    /// <summary>
    /// 当前登录的用户
    /// </summary>
    public string UserName { get; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// 用户尚未登录系统时抛出 UnauthorizedAccessException 异常
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UnauthorizedAccessException">用户尚未认证访问异常</exception>
    public string? GetUserId() => _httpContext.GetUserId<string>();
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 userName
    /// </summary>
    /// <returns></returns>
    public string GetUserName() => _httpContext.GetUserName();
    /// <summary>
    /// 获取数据隔离的数据编号
    /// </summary>
    /// <returns></returns>
    public string? GetDataIsolateId() => _httpContext.GetClaimValue<string>(AppSettings.CLAIM_TYPE_DATA_ISOLATE_ID);

    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <returns></returns>
    public async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>() where TModel : class, new() => await GetPagedResultAsync<TModel>((TimeRangePagedRequest)_request.GetPagedRequest());
    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class, new() => await _request.GetPagedResultAsync<TModel>(await GetQueryableAsync<TModel>(predicate));
    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="request">查询请求</param>
    /// <returns></returns>
    protected override async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>(TimeRangePagedRequest request)
    {
        var userName = _httpContext.GetUserName();
        request.StartTime ??= _request.GetRequestOption<DateTime>(nameof(request.StartTime));
        request.EndTime ??= _request.GetRequestOption<DateTime>(nameof(request.EndTime));
        _logger.LogDebug("用户 {userName} 将查询 {host} 的数据库 {database}", userName, _endpoint.Host, _endpoint.Database);
        var flux = GetFluxQueryString<TModel>(request);
        _logger.LogDebug($"flux query string is: {flux}");
        var queryApi = _client.GetQueryApi();
        var data = await queryApi.QueryAsync<TModel>(flux, _endpoint.Domain);
        _logger.LogInformation("用户 {userName} 已查询 {host} 的数据库 {database}, 查询结果: " + data.Count, userName, _endpoint.Host, _endpoint.Database);
        return new PagedResult<TModel>
        {
            PageIndex = request.PageIndex,
            PageSize = request.PageSize,
            Total = data.Count,
            Data = data
        };
    }
}